home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / POVSRC / SOURCE / PopupMenu.c < prev    next >
Text File  |  1994-02-04  |  13KB  |  433 lines

  1. /*==============================================================================
  2. Project:    POV-Ray
  3.  
  4. Version:    2.2
  5.  
  6. File:        PopupMenu.h
  7.  
  8. Description:
  9. Routines to handle pop-up menus in dialogs.  Long-hand way to do it,
  10. so it works in System 6 and System 7.
  11.  
  12. ------------------------------------------------------------------------------
  13. Author:
  14.     Eduard [esp] Schwan
  15. ------------------------------------------------------------------------------
  16.     from Persistence of Vision Raytracer
  17.     Copyright 1993 Persistence of Vision Team
  18. ------------------------------------------------------------------------------
  19.     NOTICE: This source code file is provided so that users may experiment
  20.     with enhancements to POV-Ray and to port the software to platforms other 
  21.     than those supported by the POV-Ray Team.  There are strict rules under
  22.     which you are permitted to use this file.  The rules are in the file
  23.     named POVLEGAL.DOC which should be distributed with this file. If 
  24.     POVLEGAL.DOC is not available or for more info please contact the POV-Ray
  25.     Team Coordinator by leaving a message in CompuServe's Graphics Developer's
  26.     Forum.  The latest version of POV-Ray may be found there as well.
  27.  
  28.     This program is based on the popular DKB raytracer version 2.12.
  29.     DKBTrace was originally written by David K. Buck.
  30.     DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  31. ------------------------------------------------------------------------------
  32. More Info:
  33.     This Macintosh version of POV-Ray was created and compiled by Jim Nitchals
  34.     (Think 5.0) and Eduard Schwan (MPW 3.2), based (loosely) on the original
  35.     port by Thomas Okken and David Lichtman, with some help from Glenn Sugden.
  36.  
  37.     For bug reports regarding the Macintosh version, you should contact:
  38.     Eduard [esp] Schwan
  39.         CompuServe: 71513,2161
  40.         Internet: jl.tech@applelink.apple.com
  41.         AppleLink: jl.tech
  42.     Jim Nitchals
  43.         Compuserve: 73117,3020
  44.         America Online: JIMN8
  45.         Internet: jimn8@aol.com -or- jimn8@applelink.apple.com
  46.         AppleLink: JIMN8
  47. ------------------------------------------------------------------------------
  48. Change History:
  49.     930228    [esp]    Created
  50.     930611    [esp]    Radical rewrite to allow multiple popups in a dialog
  51.     931001    [esp]    version 2.0 finished (Released on 10/4/93)
  52.     931119    [djh]    2.0.1 source conditionally compiles for PPC machines, keyword __powerc
  53. ==============================================================================*/
  54.  
  55. #define POPUPMENU_C
  56.  
  57. #include <types.h>
  58. #include <stdlib.h>    // malloc
  59. #include <memory.h>    // NewPtr
  60.  
  61. #if defined (__powerc)
  62. #include "PovMac.h"    // for routine descriptor with global scope
  63. #endif // __powerc
  64. #include "PopupMenu.h"
  65.  
  66. #if defined(applec)
  67. #include <Strings.h>        // p<->cstr
  68. #endif // applec
  69. #include <Fonts.h>            // checkMark
  70. #include <Resources.h>        // ReleaseResource
  71.  
  72.  
  73. // constants for positioning the default popup item within its box
  74. #define    DOWNTRI_WIDTH    12        // room on right of popup for down triangle
  75. #define    SLOP_LEFT        13        // leave this much space on left of title
  76.     
  77.  
  78. // =====================================================================
  79.  
  80. // Currently active list of popup items in current dialog
  81. static popupRecHdl_t    gCurrentListRoot;
  82.  
  83.  
  84. // =====================================================================
  85.  
  86.  
  87. //-----------------------------------------------------------
  88. static popupRecHdl_t FindPopupRec(short popupItemID)
  89. {
  90.     popupRecHdl_t    foundItem = NULL;
  91.     popupRecHdl_t    searchItem = gCurrentListRoot;
  92.  
  93.     // traverse the list, looking for a match of dlg item #s
  94.     while (searchItem && !foundItem)
  95.     {
  96.         if ((**searchItem).fPopupItemID == popupItemID)
  97.             foundItem = searchItem;
  98.         else
  99.             searchItem = (popupRecHdl_t)(**searchItem).fNext;
  100.     }
  101.     return(foundItem);
  102. } // FindPopupRec
  103.  
  104.  
  105. //-----------------------------------------------------------
  106. static void DrawDownTriangle(register short h, register short v)
  107. {
  108.     register short i;
  109.  
  110.     for (i = 0; i < (DOWNTRI_WIDTH/2); ++i)    {
  111.         MoveTo(h + (DOWNTRI_WIDTH/2)-1 - i, v - i);
  112.         Line(2*i, 0);
  113.     }
  114. }
  115.  
  116.  
  117. //-----------------------------------------------------------
  118. // Draw the popup menu (called by Dialog Mgr for updates, and by our filterproc)
  119. pascal void DrawPopupMenu(DialogPtr pDialogPtr, short pItem)
  120. {
  121. #pragma unused (pDialogPtr,pItem)
  122.     popupRecHdl_t    popupItemH;
  123.     popupRec_t        thePopupRec;
  124.     short            itemWidth;
  125.     short            itemWidthNew;
  126.     short            itemLength;
  127.     short            theItemType;
  128.     Handle            theItemH;
  129.     Rect            theItemRect;
  130.     StringPtr        textPtr;
  131.     PenState        savePen;
  132.     FontInfo        fontInfo;
  133.     Str255            theItemText;
  134.  
  135.     // find the appropriate popup item from list, given dialog item ID
  136.     popupItemH = FindPopupRec(pItem);
  137.  
  138.     if (popupItemH)
  139.     {
  140.         // use a temporary to rid us of the floating handle problem
  141.         thePopupRec = **popupItemH;
  142.  
  143.         GetPenState(&savePen);
  144.  
  145.         // get popup item box
  146.         GetDItem(thePopupRec.fParentDialog, thePopupRec.fPopupItemID,
  147.                 &theItemType, &theItemH, &theItemRect);
  148.  
  149.         // get the menu title
  150.         textPtr = (StringPtr)(**(thePopupRec.fMenuHandle)).menuData;    // Menu title
  151.  
  152.         // need to know font size for later
  153.         GetFontInfo(&fontInfo);
  154.  
  155.         // Draw the menu title off the left of popup box
  156.         MoveTo(theItemRect.left-StringWidth(textPtr)-2, theItemRect.top+fontInfo.ascent);
  157.         DrawString(textPtr);
  158.  
  159.         // Get the currently chosen popup item text
  160.         GetItem(thePopupRec.fMenuHandle, thePopupRec.fLastChoice, theItemText);
  161.  
  162.         // Insure that the item fits. Truncate it and add an ellipses (“…”) if it doesn’t
  163.         itemWidth = (theItemRect.right-theItemRect.left) - (CharWidth(checkMark)+DOWNTRI_WIDTH+4); // available string area
  164.         itemWidthNew = StringWidth(theItemText); // get current width
  165.         if (itemWidthNew > itemWidth)
  166.         {    // doesn't fit - truncate it
  167.             itemLength = theItemText[0]; // current length in characters
  168.             itemWidth = itemWidth - CharWidth('…'); // subtract width of ellipses
  169.         
  170.             do    { // until it fits (or we run out of characters)
  171.                 // drop the last character and its width
  172.                 itemWidthNew -= CharWidth(theItemText[itemLength]);
  173.                 itemLength--;
  174.             } while ((itemWidthNew > itemWidth) && (itemLength > 0));
  175.         
  176.             // add the ellipses character
  177.             itemLength++; // add room for elipsis character
  178.             theItemText[itemLength] = '…';
  179.             theItemText[0] = itemLength; // set the new true length
  180.         }
  181.  
  182.         // draw the box
  183.         PenSize(1, 1);
  184.         FrameRect(&theItemRect);
  185.         // and its drop shadow
  186.         MoveTo(theItemRect.right, theItemRect.top+2);
  187.         LineTo(theItemRect.right, theItemRect.bottom);
  188.         LineTo(theItemRect.left+2, theItemRect.bottom);
  189.  
  190.         // draw the string
  191.         MoveTo(theItemRect.left+CharWidth(checkMark)+2, theItemRect.top+fontInfo.ascent);
  192.         DrawString(theItemText);
  193.  
  194.         DrawDownTriangle(theItemRect.right-1-DOWNTRI_WIDTH, theItemRect.top+fontInfo.ascent);
  195.  
  196.         SetPenState(&savePen);
  197.     } // if (popupItemH)
  198.  
  199. } // DrawPopupMenu
  200.  
  201.  
  202. //-----------------------------------------------------------
  203. void InitPopups(void)
  204. {
  205.     gCurrentListRoot = NULL;
  206. } // InitPopups
  207.  
  208.  
  209. //-----------------------------------------------------------
  210. Boolean PopupsExist(void)
  211. {
  212.     return(gCurrentListRoot != NULL);
  213. } // PopupsExist
  214.  
  215.  
  216. //-----------------------------------------------------------
  217. static OSErr FillInPopupInfo(popupRecHdl_t newItemH)
  218. {
  219.     OSErr            anError = noErr;
  220.     short            itemWidth;
  221.     short            theItemType;
  222.     Handle            theItemH;
  223.     Rect            theItemRect;
  224.     StringPtr        textPtr;
  225.     FontInfo        fontInfo;
  226.  
  227.     // we're about to dereference the heck out of this...
  228.     HLock((Handle)newItemH);
  229.  
  230.     SetPort((**newItemH).fParentDialog);
  231.  
  232.     // get the menu & attach to our rec
  233.     (**newItemH).fMenuHandle = GetMenu((**newItemH).fMenuID); // our popUp menu
  234.     if ((**newItemH).fMenuHandle == NULL)
  235.         anError = ResError();
  236.     else
  237.     {
  238.         // checkmark the current item
  239.         SetItemMark((**newItemH).fMenuHandle, (**newItemH).fLastChoice, checkMark);
  240.  
  241.         // get dialog item & adjust its rectangle
  242.         GetDItem((**newItemH).fParentDialog, (**newItemH).fPopupItemID,
  243.                 &theItemType, &theItemH, &theItemRect);
  244.         CalcMenuSize((**newItemH).fMenuHandle);
  245.         itemWidth = (**((**newItemH).fMenuHandle)).menuWidth;
  246.         itemWidth += DOWNTRI_WIDTH + 4;
  247.         if (itemWidth < (theItemRect.right - theItemRect.left))
  248.             theItemRect.right = theItemRect.left + itemWidth;
  249.         GetFontInfo(&fontInfo);
  250.         theItemRect.bottom = theItemRect.top
  251.                             + fontInfo.ascent
  252.                             + fontInfo.descent
  253.                             + fontInfo.leading
  254.                             + 1;
  255.  
  256.         // remember popup bounds
  257.         (**newItemH).fPopupBounds = theItemRect;
  258.  
  259.         // set items rectangle & install handler routine (DrawPopupMenu)
  260. #if defined(__powerc)
  261.         SetDItem((**newItemH).fParentDialog, (**newItemH).fPopupItemID,
  262.                 theItemType, (Handle)&gPopupMenuRD, &theItemRect);
  263. #else
  264.         SetDItem((**newItemH).fParentDialog, (**newItemH).fPopupItemID,
  265.                 theItemType, (Handle)DrawPopupMenu, &theItemRect);
  266. #endif // __powerc
  267.         // now figure out title bounds
  268.         // get the menu title
  269.         textPtr = (StringPtr)(**((**newItemH).fMenuHandle)).menuData;    // Menu title
  270.         theItemRect.right = theItemRect.left - 1;
  271.         theItemRect.left = theItemRect.right - StringWidth(textPtr) - 2;
  272.         (**newItemH).fTitleBounds = theItemRect;
  273.     }
  274.  
  275.     HUnlock((Handle)newItemH);
  276.  
  277.     return(anError);
  278.  
  279. } // FillInPopupInfo
  280.  
  281.  
  282. //-----------------------------------------------------------
  283. void AddPopupToList(popupRecPtr_t newPopupRecP)
  284. {
  285.     OSErr            anError = noErr;
  286.     popupRecHdl_t    newItemH;
  287.     popupRecHdl_t    nextItemH;
  288.  
  289.     // allocate new popup item
  290.     newItemH = (popupRecHdl_t)NewHandle(sizeof(popupRec_t));
  291.  
  292.     if (newItemH)
  293.     {
  294.         // copy user info into it
  295.         **newItemH = *newPopupRecP;
  296.         // fill in additional info from menu etc.
  297.         anError = FillInPopupInfo(newItemH);
  298.         // remember list contents, append it later
  299.         nextItemH = gCurrentListRoot;
  300.         // point the head of list to our new item
  301.         gCurrentListRoot = newItemH;
  302.         // connect rest of list after our newly inserted item
  303.         (**newItemH).fNext = (Handle)nextItemH;
  304.     }
  305. } // AddPopupToList
  306.  
  307.  
  308. //-----------------------------------------------------------
  309. void KillPopups(void)
  310. {
  311.     popupRecHdl_t    trailerH = gCurrentListRoot;
  312.  
  313.     // walk the list of popup recs & dispose of each one
  314.     while (gCurrentListRoot != NULL)
  315.     {
  316.         trailerH = gCurrentListRoot;
  317.         gCurrentListRoot = (popupRecHdl_t)(**gCurrentListRoot).fNext;
  318.         // release the attached menu resource
  319.         if ((**trailerH).fMenuHandle)
  320.             ReleaseResource((Handle)(**trailerH).fMenuHandle);
  321.         DisposeHandle((Handle)trailerH);
  322.     }
  323.     gCurrentListRoot = NULL;
  324. } // KillPopups
  325.  
  326.  
  327. //-----------------------------------------------------------
  328. // Filterproc for popup userItem hits on mouse down (call from your dialog filter proc)
  329. pascal Boolean PopupMouseDnDlgFilterProc(DialogPtr pDialogPtr, EventRecord *pEventPtr, short *pItemHitPtr)
  330. {
  331.     Point        mouseLoc,
  332.                 popLoc;
  333.     short        newChoice,
  334.                 theItem;
  335.     long        chosen;
  336.     Boolean        myFilter;
  337.     popupRecHdl_t    popupItemH = NULL;
  338.  
  339.     // pre-initialize return values
  340.     *pItemHitPtr = 0;
  341.     myFilter = false; // haven't handled yet
  342.  
  343.     if (pEventPtr->what == mouseDown)
  344.     {
  345.         mouseLoc = pEventPtr->where; // copy the mouse position
  346.         GlobalToLocal(&mouseLoc); // convert it to local dialog coordinates
  347.         
  348.         // Was the click in a popup item?  NOTE: FindDItem is zero-based!
  349.         theItem = FindDItem(pDialogPtr, mouseLoc)+1; // FindDialogItem someday...
  350.         if (theItem >= 0)
  351.             popupItemH = FindPopupRec(theItem);
  352.  
  353.         if (popupItemH)
  354.         {
  355.             HLock((Handle)popupItemH);
  356.             // It's time to pop up the menu
  357.             // - Insert the menu into the menu list,
  358.             // - call CalcMenuSize (to work around a bug in the Menu Manager)
  359.             // - call popupRecSelect and let the user drag around
  360.             // Note that the (top,left) parameters to popupRecSelect are
  361.             // our item’s, converted to global coordinates.
  362.  
  363.             // hilight the title
  364.             InvertRect(&(**popupItemH).fTitleBounds);
  365.  
  366.             // insert our menu in the menu list
  367.             InsertMenu((**popupItemH).fMenuHandle, -1);
  368.  
  369.             // copy our item’s topleft
  370.             popLoc = *(Point*)(&(**popupItemH).fPopupBounds.top);
  371.             LocalToGlobal(&popLoc); // convert back to global coords
  372.             CalcMenuSize((**popupItemH).fMenuHandle); // Work around Menu Mgr bug
  373.  
  374.             chosen = PopUpMenuSelect((**popupItemH).fMenuHandle,
  375.                             popLoc.v, popLoc.h, (**popupItemH).fLastChoice);
  376.  
  377.             // Remove our menu from the menu list
  378.             DeleteMenu((**popupItemH).fMenuID);
  379.  
  380.             // unhilight the title
  381.             InvertRect(&(**popupItemH).fTitleBounds);
  382.             
  383.             // Was something chosen?
  384.             if (chosen)
  385.             {
  386.                 // get the chosen item number
  387.                 newChoice = chosen & 0x0000ffff;
  388.                 if (newChoice != (**popupItemH).fLastChoice)
  389.                 {
  390.                     // the user chose an item other than the current one
  391.                     // unmark old choice
  392.                     SetItemMark((**popupItemH).fMenuHandle, (**popupItemH).fLastChoice, noMark);
  393.                     (**popupItemH).fLastChoice = newChoice; // update the current choice
  394.                     // mark the new choice
  395.                     SetItemMark((**popupItemH).fMenuHandle, (**popupItemH).fLastChoice, checkMark);
  396.                     
  397.                     // Draw the newly selected item
  398.                     EraseRect(&(**popupItemH).fPopupBounds);
  399.                     DrawPopupMenu(pDialogPtr, (**popupItemH).fPopupItemID);
  400.                     
  401.                     myFilter = true; // dialog is over
  402.                     // have ModalDialog return that the user changed items
  403.                     *pItemHitPtr = (**popupItemH).fPopupItemID;
  404.                 } // if new choice
  405.             } // if chosen
  406.  
  407.             HUnlock((Handle)popupItemH);
  408.         } // if our popup Item
  409.     } // if mousedown
  410.  
  411.     return myFilter;
  412.  
  413. } // PopupMouseDnDlgFilterProc
  414.  
  415.  
  416.  
  417. //-----------------------------------------------------------
  418. short GetPopupValue(short pPopupItemID)
  419. {
  420.     short            theValue = 0;
  421.     popupRecHdl_t    popupItemH;
  422.  
  423.     // find the popup rec
  424.     popupItemH = FindPopupRec(pPopupItemID);
  425.  
  426.     if (popupItemH)
  427.         theValue = (**popupItemH).fLastChoice;
  428.  
  429.     return(theValue);
  430. } // GetPopupValue
  431.  
  432.  
  433.